package com.labofjet.common.util;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 注解工具类
 *
 * @author jyzjyz12@163.com
 */
public class AnnotationUtils {
	private static final Map<Class<?>, Map<Class<? extends Annotation>, List<Field>>> FIELD_CACHE = new ConcurrentHashMap<Class<?>, Map<Class<? extends Annotation>, List<Field>>>();
	private static final Map<Class<?>, Map<Class<? extends Annotation>, List<Method>>> METHOD_CACHE = new ConcurrentHashMap<Class<?>, Map<Class<? extends Annotation>, List<Method>>>();

	private AnnotationUtils() {

	}

	/**
	 * 返回当前类上的注解,包括继承下来的,用的比较多
	 *
	 * @param targetClass
	 * @return
	 */
	public static List<Annotation> getAnnotationInClass(Class<?> targetClass) {
		return Arrays.asList(targetClass.getAnnotations());
	}

	/**
	 * 返回当前类以及父类上的注解,去掉重复的,可能没啥用
	 *
	 * @param targetClass
	 * @return
	 */
	public static List<Annotation> getAnnotationInClassAndSuperClass(Class<?> targetClass) {
		List<Annotation> result = new ArrayList<Annotation>();
		result.addAll(getAnnotationInClass(targetClass));
		while (targetClass.getSuperclass() != null && targetClass.getSuperclass() != Object.class) {
			targetClass = targetClass.getSuperclass();
			result.addAll(getAnnotationInClass(targetClass));
		}
		result = new ArrayList<Annotation>(new HashSet<Annotation>(result));//去重
		return result;
	}

	/**
	 * 查找当前类以及父类中含有给定注解的Field,有缓存
	 *
	 * @param targetClass
	 * @param annotation
	 * @return
	 */
	public static List<Field> getFieldInClassAndSuperClassByAnnotation(Class<?> targetClass, Class<? extends Annotation> annotation) {
		if (FIELD_CACHE.get(targetClass) != null && FIELD_CACHE.get(targetClass).get(annotation) != null) {//有缓存
			return FIELD_CACHE.get(targetClass).get(annotation);
		} else {//无缓存
			List<Field> resultList = new ArrayList<Field>();
			Map<Class<? extends Annotation>, List<Field>> map = new HashMap<Class<? extends Annotation>, List<Field>>();
			getAnnotationFieldInClassAndSuperClass(resultList, annotation, targetClass);
			map.put(annotation, resultList);
			FIELD_CACHE.put(targetClass, map);
			return resultList;
		}
	}

	/**
	 * 查找当前类以及父类中含有给定注解的Method,有缓存
	 *
	 * @param targetClass
	 * @param annotation
	 * @return
	 */
	public static List<Method> getMethodInClassAndSuperClassByAnnotation(Class<?> targetClass, Class<? extends Annotation> annotation) {
		if (METHOD_CACHE.get(targetClass) != null && METHOD_CACHE.get(targetClass).get(annotation) != null) {//有缓存
			return METHOD_CACHE.get(targetClass).get(annotation);
		} else {//无缓存
			List<Method> resultList = new ArrayList<Method>();
			Map<Class<? extends Annotation>, List<Method>> map = new HashMap<Class<? extends Annotation>, List<Method>>();
			getAnnotationMethodInClassAndSuperClass(resultList, annotation, targetClass);
			map.put(annotation, resultList);
			METHOD_CACHE.put(targetClass, map);
			return resultList;
		}
	}

	private static void getAnnotationFieldInClassAndSuperClass(List<Field> resultList, Class<? extends Annotation> annotation,
	                                                           Class<?> targetClass) {
		//查找本类中的所有Field
		for (Field field : targetClass.getDeclaredFields()) {
			if (field.isAnnotationPresent(annotation)) {
				resultList.add(field);
			}
		}
		//递归查找父类中的所有Field
		if (targetClass.getSuperclass() != null && targetClass.getSuperclass() != Object.class) {
			getAnnotationFieldInClassAndSuperClass(resultList, annotation, targetClass.getSuperclass());
		}
	}

	private static void getAnnotationMethodInClassAndSuperClass(List<Method> resultList, Class<? extends Annotation> annotation,
	                                                            Class<?> targetClass) {
		//查找本类中的所有method
		for (Method method : targetClass.getDeclaredMethods()) {
			if (method.isAnnotationPresent(annotation)) {
				resultList.add(method);
			}
		}
		//递归查找父类中的所有method
		if (targetClass.getSuperclass() != null && targetClass.getSuperclass() != Object.class) {
			getAnnotationMethodInClassAndSuperClass(resultList, annotation, targetClass.getSuperclass());
		}
	}
}
